# To change this template, choose Tools | Templates
# and open the template in the editor.

__author__="MrEction"
__date__ ="$Feb 8, 2011 11:38:20 AM$"
# To change this template, choose Tools | Templates
# and open the template in the editor.

__author__="MrEction"
__date__ ="$Jan 27, 2011 4:12:10 PM$"


import pygame
from pygame.draw import *
from hexconfig import *
import random

import pickle


THINKSPEED = 1
HEXSIDELEN = 14
BOARDSIZE = 8
GRIDORIGIN = (400.0,300.0)
HEXORIGIN = (GRIDORIGIN[0]+dx(HEXSIDELEN,math.radians(60.0)),\
            GRIDORIGIN[1]+dy(HEXSIDELEN,math.radians(60.0)))
LINETHICK = 2


GRIDXLEN = 10
GRIDYLEN = 10
GRIDZLEN = 10


DEBUGCIRCLELOCS = False

BOARDCOLOR = (245,245,245)

RL_BETA = 0.85
FRUIT_AWARD = 0.1

UTIL_MINVAL = 0.00005

"""
              ____
          /    \\
     ____/ 0 1-1\\____
         \      //    \
   -1 1 0 \____// 1 0-1\___
          /    \\      /
     ____/ 0 0 0\\____/ 2 -1 -1
         \      //    \
   -1 0 1 \____// 1-1 0\__
          /    \\      /
     ____/ 0-1 1\\____/
         \      //    \
          \____//      \

    the flower down is recursively adding the the bottom,
    until any values hit extremes

    """
#THIS IS A LIST OF EM STARTING TOP FIRST AND GOING CC
HEXDIRECTIONS = [(0,1,-1),(-1,1,0),(-1,0,1),(0,-1,1),(1,-1,0),(1,0,-1)]
UP = 0




def getPosFromHexLoc(hexLoc,drawable = False):
    pos = [HEXORIGIN[0],HEXORIGIN[1]]
    l = HEXSIDELEN


    #X COMPONENT
    pos[0]+=l*hexLoc[0]
    #YCOMPONENT
    pos[0]+=dx(l*hexLoc[1],math.radians(-120.0))
    pos[1]+=dy(l*hexLoc[1],math.radians(-120.0))
    #ZCOMPONENT
    pos[0]+=dx(l*hexLoc[2],math.radians(120.0))
    pos[1]+=dy(l*hexLoc[2],math.radians(120.0))

    if drawable:
        pos = [int(pos[0]),int(pos[1])]
    return pos

def getGridLocFromMouse(mouseloc):
   # /* doubles x, y, and z hold the initial raw ungridded position */

  x,y = mouseloc
  z = 0
  ix = int(x)
  iy = int(y)
  iz = int(z)
  rx = int(x)
  ry = int(y)
  rz = int(z)

  s = ix+iy+iz

  if s :
    abs_dx = math.fabs(rx-x),
    abs_dy = math.fabs(ry-y)
    abs_dz = math.fabs(rz-z);

    if abs_dx >= abs_dy and abs_dx >= abs_dz:
        ix -= s; # abs_dx is max.
    elif abs_dy >= abs_dx and abs_dy >= abs_dz:
      iy -= s; #/* abs_dy is max. *.
    else: iz-=s;

  print "get gridloc: ",(ix,iy,iz)
  return (ix,iy,iz)

  #/* ix, iy, iz now hold the coordinates of the hex. */






class Hexnake():
    def __init__(self):
        self.body = []
        self.length = []
        self.pdirection = UP

    def update(self,etime):
        return

    def draw(self,surf):
        for b in self.body:
            pass
IDLE = 0
PATROLLING = 1

class Enemy():
    def __init__(self):
        self.hexloc = [0,0,0]
        self.viewSpace = []
        self.state = IDLE
        self.alertness = 0

    def update(self,etime):
        self.rotateView()
        self.checkPlayerVisibility()
        return



    #todo-
    def rotateView(self):

        for v in self.viewSpace:
            pass#figure out how to rotate spacially around origin pt
                #rotation pt is body


        #Alternate possibility is to use multiple direction baked viewspaces
        #that multiple enemies can share from... do this
        return

    #check players body segments for collision with viewspace
    def checkPlayerVisibility(self):
        return
    def draw(self,surf):
        circle(surf,(0,0,0),getPosFromHexLoc(self.hexloc,True),8)
        circle(surf,(255,0,0),getPosFromHexLoc(self.hexloc,True),5)

    def pathfind(self):

        openSet  = [startNode]
        closedSet = []


        lowNode = None
        while( not lowNode == Goal):
            current = self.removeLowestRankedNode(openSet)
            closedSet.append(current)
            for nbr in self.nbrList(current.loc):
                cost = g(current)

class HexWorld():
    
    def __init__(self):
        self.setup()

    def setup(self):

       
        #print SWIDTH,",",SHEIGHT
        self.screen = pygame.display.set_mode((SWIDTH,SHEIGHT))#,pygame.HWSURFACE)
        pygame.display.set_caption("HEXworld")

        pygame.event.set_grab(False)
        self.clock = pygame.time.Clock()
        #pygame.font.initialize()

        self.keyPressDict = {}
        for k in KEYLIST:
            self.keyPressDict[k] = False
        self.debugMode = False


        self.mouseHold = False

        self.state = IDLE

        self.gridSetup()

        #self.discoFlag STUFF
        self.gridanimtick  = 0.0
        self.animGridList = []
        self.animspd = 100
        self.discoFlag =False
        #self.discoFlag STUFF

        self.playerSetup()
        

        self.actionCounter = 0
        self.zoneList =[]

        self.aiSetup()

        self.pregameState = True

    def propagateReward(self,reward):
        t = 0.0
        for pair in self.moveStack:

            util = (math.pow(RL_BETA,t))#*reward if not t == 0 else reward)
            if util<UTIL_MINVAL:
                break
            self.utilityDict[pair[0]][pair[1]] +=util
            t+=1.0
        print self.utilityDict
        self.playerSetup()
        self.moveStack = []

        
    def aiUpdate(self,etime):
        self.aiTimer+=etime
        if self.aiTimer>=THINKSPEED:
            self.aiThink()
            self.aiTimer = 0

    def bestDirByUtil(self,dirs):
        maxUtil = -1.0
        bestDir = 0
        pairs = []
        """
        for dirI in dirs:
            dir = HEXDIRECTIONS[dirI]
            #newLoc = (self.ploc[0]+dir[0],self.ploc[1]+dir[1],self.ploc[2]+dir[2])
            if newLoc in self.gridLocs:
                pairs.append((newLoc,dirI))
        
        """

        equalList = []
        #maxUtil = max(self.utilityDict[self.ploc])

        for dir in dirs:
            if self.utilityDict[self.ploc][dir]>maxUtil:
                maxUtil = self.utilityDict[self.ploc][dir]
                bestDir = dir
        for dir in dirs:
            if self.utilityDict[self.ploc][dir] == maxUtil:
                equalList.append(dir)

        #print equalList

        return random.choice(equalList)

    def aiThink(self):
        #totally random dir
        moves = self.getPossibleMoves()

        #print moves

        #random


        if random.random()<0.25:
            self.pdir = random.choice(moves)
        #bestUtil
        else:
            self.pdir = self.bestDirByUtil(moves)
        #self.pdir =
        #this might not work because ploc is not a tuple
        self.moveStack.insert(0,(self.ploc,self.pdir))

        self.movePlayer()

        if self.ploc == self.foodLoc:
            self.propagateReward(FRUIT_AWARD)





    def aiSetup(self):
        self.utilityDict = {}
        for loc in self.gridLocs:
            self.utilityDict[loc] = []
            for dir in range(6):
                self.utilityDict[loc].append(0.0)




        self.moveStack = []
        self.beta = 0.5
        self.learningStyle = "Reinforcement Learning"
        self.foodLoc = (0,-BOARDSIZE,BOARDSIZE)
        self.aiTimer = 0
        #maybe implement q


        

    def getPossibleMoves(self):
        #returns a list of dirs in available locations relative to player heading
        dirs = []
        dirs.append((self.pdir-1) if self.pdir-1>=0 else 5)  #left
        dirs.append((self.pdir+1)%6)
        dirs.append(self.pdir)
        retList=[]
        for dirIndex in dirs:
            dir = HEXDIRECTIONS[dirIndex]
            #this is almost stupid because those pts arent in the gridlocs... leave in until proof
            #a lot of text hehe
            if math.fabs(self.ploc[0]+dir[0])>BOARDSIZE+1 or math.fabs(self.ploc[1]+dir[1])>BOARDSIZE+1 \
                                            or math.fabs(self.ploc[2]+dir[2])>BOARDSIZE+1:
                continue

            newLoc = (self.ploc[0]+dir[0],self.ploc[1]+dir[1],self.ploc[2]+dir[2])
            if newLoc in self.gridLocs:
                retList.append(dirIndex)

        if retList == []:
            #DEAD END SITUATION
            if random.random()<0.5:
                retList.append((self.pdir+2)%6)
            else:
                newDir = self.pdir-2
                if newDir<0:
                    newDir+=6
                retList.append(newDir)

        return retList
                



    def gridSetup(self):
        self.gridLocs = []
        """
        for x in range(GRIDXLEN):
            self.gridLocs.append([])
            for y in range(GRIDYLEN):
                self.gridLocs[x].append([])
                for z in range(GRIDZLEN):

                    loc = 0
                    self.gridLocs[x][y].append(loc)
        """
        self.gridLocs = self.getFlowerOutGrid()


    def playerSetup(self):
        self.ploc = (0,BOARDSIZE,-BOARDSIZE)
        self.pbody= []
        self.pdir = 3
        self.bodyLen = 10
        self.keyRefreshFlags = {"UP":False,"LEFT":False,"DOWN":False,"RIGHT":False}

        return

    """
          /    \\
     ____/ 0 1-1\\____
         \      //    \
   -1 1 0 \____// 1 0-1\___
          /    \\      /
     ____/ 0 0 0\\____/ 2 -1 -1
         \      //    \
   -1 0 1 \____// 1-1 0\__
          /    \\      /
     ____/ 0-1 1\\____/
         \      //    \
          \____//      \
    """
    def movePlayer(self):
        dir = HEXDIRECTIONS[self.pdir]
        if math.fabs(self.ploc[0]+dir[0])>BOARDSIZE+1 or math.fabs(self.ploc[1]+dir[1])>BOARDSIZE+1 \
                                            or math.fabs(self.ploc[2]+dir[2])>BOARDSIZE+1:
            return
        
        self.pbody.insert(0,(self.ploc[0],self.ploc[1],self.ploc[2]))
        if len(self.pbody)>self.bodyLen:
            self.pbody = self.pbody[0:self.bodyLen]


        self.ploc = (self.ploc[0]+dir[0],self.ploc[1]+dir[1],self.ploc[2]+dir[2])

        self.actionCounter+=1

    def turnPlayer(self,amt):
        self.pdir = (self.pdir+amt)%6
        if self.pdir<0:
            self.pdir+=6



    def getFlowerOutGrid(self):
        origincoord = (0,0,0)
        coordList = [origincoord]
        self.flowerOutNewHexs(origincoord, coordList)
        print "FlowerOut Successful:"
        return coordList

    def flowerOutNewHexs(self,coord,list):
        extreme = BOARDSIZE
        for val in coord:
            if math.fabs(val)>extreme:
                return
        newcoords = []

        newcoords.append((coord[0]-1,coord[1],coord[2]+1))
        newcoords.append((coord[0],coord[1]-1,coord[2]+1))
        newcoords.append((coord[0]+1,coord[1]-1,coord[2]))
        newcoords.append((coord[0]-1,coord[1]+1,coord[2]))
        newcoords.append((coord[0],coord[1]+1,coord[2]-1))
        newcoords.append((coord[0]+1,coord[1],coord[2]-1))
        for nc in newcoords:
            if not nc in list:
                list.append(nc)
                self.flowerOutNewHexs(nc,list)


    def getFlowerDownGrid(self):
        origincoord = (0,0,0)
        coordList = [origincoord]
        self.flowerDownNewHexs(origincoord, coordList)
        print "FlowerDown Successful:"
        return coordList

    def flowerDownNewHexs(self,coord,list):
        extreme = BOARDSIZE
        for val in coord:
            if math.fabs(val)>extreme:
                return
        newcoords = []

        newcoords.append((coord[0]-1,coord[1],coord[2]+1))
        newcoords.append((coord[0],coord[1]-1,coord[2]+1))
        newcoords.append((coord[0]+1,coord[1]-1,coord[2]))

        for nc in newcoords:
            if not nc in list:
                list.append(nc)
                self.flowerDownNewHexs(nc,list)

    

    def drawHexGridBG(self,surf):
        for loc in self.gridLocs:
            self.drawHexFill(surf,BOARDCOLOR,getPosFromHexLoc(loc),False)
    #def getHex
    def drawHexGrid(self,surf):
        
        for loc in self.gridLocs:
            
            if math.fabs(loc[0])>BOARDSIZE or math.fabs(loc[1])>BOARDSIZE \
                                            or math.fabs(loc[2])>BOARDSIZE:
                self.drawHexBorder(surf,getPosFromHexLoc(loc),False)
            else:
                self.drawHexTop(surf,getPosFromHexLoc(loc),False)

        return

    def drawHexTop(self,surf,loc,topleft = True):
        l = HEXSIDELEN
        if topleft:
            pts = [(),(),(),()]
            pts[0] = loc
            pts[1] = (loc[0]+l,loc[1])
            pts[2] = (pts[1][0]+dx(l,math.radians(60.0)),pts[1][1]+dy(l,math.radians(60.0)))
            pts[3] = (pts[0][0]+dx(l,math.radians(120.0)),pts[0][1]+dy(l,math.radians(60.0)))

            for pt in pts:
                pt = (int(pt[0]),int(pt[1]))
            line(surf,(0,0,0),pts[0],pts[1],LINETHICK)
            line(surf,(0,0,0),pts[1],pts[2],LINETHICK)
            line(surf,(0,0,0),pts[0],pts[3],LINETHICK)
        else:
            
            topleftpt = (loc[0]+dx(l,math.radians(-120.0)),loc[1]+dy(l,math.radians(-120.0)))
            self.drawHexTop(surf, topleftpt, True)
        return
    def drawHexBorder(self,surf,loc,topleft = True):
        l = HEXSIDELEN
        if not topleft:
            topleftpt = (loc[0]+dx(l,math.radians(-120.0)),loc[1]+dy(l,math.radians(-120.0)))
            self.drawHexBorder(surf, topleftpt, True)
        else:
            pts = [(),(),(),(),(),()]
            pts[0] = loc
            pts[1] = (loc[0]+l,loc[1])
            pts[2] = (pts[1][0]+dx(l,math.radians(60.0)),pts[1][1]+dy(l,math.radians(60.0)))
            pts[3] = (pts[2][0]+dx(l,math.radians(120.0)),pts[2][1]+dy(l,math.radians(120.0)))
            pts[4] = (pts[3][0]-l,pts[3][1])
            pts[5] = (pts[0][0]+dx(l,math.radians(120.0)),pts[0][1]+dy(l,math.radians(60.0)))

            for pt in pts:
                pt = (int(pt[0]),int(pt[1]))
            for i in range(len(pts)):
                line(surf,(0,0,0),pts[i],pts[(i+1)%6],LINETHICK)
            #polygon(surf,color,pts)
    def drawDirPoly(self,surf,loc,dir):
        pt1 = getPosFromHexLoc(loc)
        loc2 = [loc[0]+dir[0],loc[1]+dir[1],loc[2]+dir[2]]
        pt2 = getPosFromHexLoc(loc2)
        line(surf,(255,0,0),pt1,pt2,2)
        return
    def drawHexFill(self,surf,color,loc,topleft = True):
        l = HEXSIDELEN
        if not topleft:
            topleftpt = (loc[0]+dx(l,math.radians(-120.0)),loc[1]+dy(l,math.radians(-120.0)))
            self.drawHexFill(surf,color, topleftpt, True)
        else:
            pts = [(),(),(),(),(),()]
            pts[0] = loc
            pts[1] = (loc[0]+l,loc[1])
            pts[2] = (pts[1][0]+dx(l,math.radians(60.0)),pts[1][1]+dy(l,math.radians(60.0)))
            pts[3] = (pts[2][0]+dx(l,math.radians(120.0)),pts[2][1]+dy(l,math.radians(120.0)))
            pts[4] = (pts[3][0]-l,pts[3][1])
            pts[5] = (pts[0][0]+dx(l,math.radians(120.0)),pts[0][1]+dy(l,math.radians(60.0)))
            bpts = []
            for pt in pts:
                pt = (int(pt[0]),int(pt[1]))
                bpts.insert(0,pt)
            polygon(surf,color,pts)
    def tick(self):
        etime = self.clock.tick()
        for e in pygame.event.get():
            self._handle_event(e)
        self.updateAll(etime)
        self.drawAll(self.screen)

        return


    
    def updateAll(self,etime):
        if self.pregameState:
            return

        if self.discoFlag:
            self.discoUpdate(etime)

        self.aiUpdate(etime)

        return
        

    def checkMouse(self,pos):
        if not self.pregameState:
            return
        self.checkMouseToGridLocs(pos)

        return
    def checkMouseToGridLocs(self,pos):
        buttonRad = HEXSIDELEN
        if self.keyPressDict["SHIFT"]:
            for loc in self.gridLocs:
                pt2 = getPosFromHexLoc(loc, False)
                if dist(pos,pt2)<buttonRad:
                    print "clicked on loc (",loc,")"

                    self.gridLocs.remove(loc)
                    del self.utilityDict[loc]
                    #if not loc in self.zoneList:
                    #    self.zoneList.append(loc)
                    return loc
    def drawUtilityVals(self,surf):
        totalSurf = surf.copy()
        totalSurf.set_colorkey((255,0,255));
        totalSurf.fill((255,0,255))
        for loc in self.utilityDict.keys():
            utilList = self.utilityDict[loc]
            maxVal = max(utilList)
            if maxVal>1.0:
                maxVal = 1.0

            col = 255-(maxVal*255)

            self.drawHexFill(surf, (col,255,255), getPosFromHexLoc(loc,True), False)


    def drawAll(self,surf):

        surf.fill((230,230,230))
        self.drawHexGridBG(surf)
        self.drawUtilityVals(surf)
        if self.discoFlag:
            for hexloc in self.animGridList:
                self.drawHexFill(surf, hexloc[1], getPosFromHexLoc(hexloc[0],True), False)
        self.drawHexFill(surf, (128,255,0), getPosFromHexLoc(self.ploc, True), False)
        for loc in self.zoneList:
            self.drawHexFill(surf,(255,255,0),getPosFromHexLoc(loc,True),False)
        for loc in self.pbody:
            
            self.drawHexFill(surf, (128,255,0), getPosFromHexLoc(loc, True), False)
        circle(surf,(100,228,0),getPosFromHexLoc(self.ploc, True),8,1)
        self.drawDirPoly(surf, self.ploc, HEXDIRECTIONS[self.pdir])

        if DEBUGCIRCLELOCS:
            for hexloc in self.gridLocs:
               circle(surf,(255,0,0),getPosFromHexLoc(hexloc,True),2)


        self.drawHexFill(surf,(0,0,255),getPosFromHexLoc(self.foodLoc,True),False)
        self.drawHexGrid(surf)

        
        pygame.display.flip()

    def saveZone(self):
        if self.zoneList == []:
            print "No Zone to save!"
            return
        name = ask(self.screen,"Save Zone As?")

        if name == "" or name == "x":
            return
        saveFile = open("../zones/"+name+".pkl",'wb')
        pickle.dump(self.zoneList,saveFile)




    def _handle_event(self,event):
        #super._handle_event(self,event)
        if event.type == pygame.QUIT:
            pygame.quit()
            #raise EndOfGameException("Quit")
            exit(0)
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                pygame.quit()
                exit(0)
                #raise EndOfGameException("Quit")
            if event.key == pygame.K_d:
                self.discoFlag = not self.discoFlag
            if event.key == pygame.K_RETURN:
                self.executeJumps()
            if event.key == pygame.K_LSHIFT:
                self.keyPressDict['SHIFT'] = True
            if event.key == pygame.K_DOWN:
                self.keyPressDict['DOWN'] = True
            if event.key == pygame.K_UP:
                self.movePlayer()
                self.keyPressDict['UP'] = True
            if event.key == pygame.K_LEFT:
                self.turnPlayer(1)
                self.movePlayer()
                self.keyPressDict['LEFT'] = True
            if event.key == pygame.K_SPACE:
                self.pregameState = False
                #self.moveEnemies()
            if event.key == pygame.K_RIGHT:
                self.turnPlayer(-1)
                self.movePlayer()
                self.keyPressDict['RIGHT'] = True
            if event.key == pygame.K_s:
                self.keyPressDict['DOWN'] = True
            if event.key == pygame.K_w:
                self.keyPressDict['UP'] = True
            if event.key == pygame.K_a:
                self.keyPressDict['LEFT'] = True
            if event.key == pygame.K_d:
                self.keyPressDict['RIGHT'] = True
            if event.key == pygame.K_j:
                self.testCalcJumpDisp()
            if event.key == pygame.K_F5:
                self.saveZone()
        if event.type == pygame.KEYUP:
            if event.key == pygame.K_DOWN:
                self.keyPressDict['DOWN'] =False
            if event.key == pygame.K_UP:
                self.keyPressDict['UP'] = False
            if event.key == pygame.K_LEFT:
                self.keyPressDict['LEFT'] = False
            if event.key == pygame.K_RIGHT:
                self.keyPressDict['RIGHT'] =False
            if event.key == pygame.K_s:
                self.keyPressDict['DOWN'] = False
            if event.key == pygame.K_w:
                self.keyPressDict['UP'] = False
            if event.key == pygame.K_a:
                self.keyPressDict['LEFT'] = False
            if event.key == pygame.K_d:
                self.keyPressDict['RIGHT'] = False
            if event.key == pygame.K_LSHIFT:
                self.keyPressDict['SHIFT'] = False

        if event.type== pygame.MOUSEBUTTONDOWN:
            if event.button == 1:
                self.mouseHold = True
                self.checkMouse(pygame.mouse.get_pos())
                #loc = getGridLocFromMouse(pygame.mouse.get_pos())

        if event.type == pygame.MOUSEBUTTONUP:
            if event.button == 1:
                self.mouseHold = False

        if event.type == pygame.MOUSEMOTION:
            if self.mouseHold:
                self.checkMouse(pygame.mouse.get_pos())
            pass

    def discoUpdate(self,etime):
        self.gridanimtick+=etime
        if self.gridanimtick>self.animspd:
            self.animGridList = random.sample(self.gridLocs,random.randrange(20,70))
            for i in range(len(self.animGridList)):
                self.animGridList[i]=(self.animGridList[i],(random.choice(RAINBOW2)))
            self.gridanimtick = 0.0
            self.animspd +=random.randrange(-5,10)
            if self.animspd<25 or self.animspd>175:
                self.animspd = 100
if __name__ == "__main__":

    engine = HexWorld()
    print "welcome,sit down and stay for a while(True)"
    while(True):
        engine.tick()



